/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hu.tothgya.timetable.utils;

import hu.tothgya.timetable.Connection;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This is a utility class to sort stops. The inputs are connections. The sorter
 * tries to create a list of stops in which all connection's start preceeds its
 * destination.
 *
 * @author tothgya
 */
public class StopSorter {

    private List<Connection> connections = new LinkedList<Connection>();

    /**
     * Creates an StopSorter.
     */
    public StopSorter() {
    }

    /**
     * Adds a connection the stop list.
     * @param c a connection
     */
    public void addConnection(Connection c) {
        connections.add(c);
    }

    /**
     * Add connections to the stop list.
     * @param list a list of connections
     */
    public void addConnection(List<Connection> list) {
        connections.addAll(list);
    }

    private enum ConnectionValidationResult {

        CORRECT, MISSING_START, MISSING_DESTINATION, WRONG_ORDER, MISSING_CONNECTION
    }

    private class StopSorterTable {

        public ArrayList<Set<String>> stopList = new ArrayList<Set<String>>();

        public ConnectionValidationResult isValid(Connection c) {
            Integer startLevel = null;
            Integer destinationLevel = null;
            int i = 0;
            for (Set<String> s : stopList) {
                for (String stopName : s) {
                    if (c.start.equals(stopName)) {
                        startLevel = i;
                    } else if (c.destination.equals(stopName)) {
                        destinationLevel = i;
                    }
                }
                i++;
            }
            if (destinationLevel == null && startLevel == null) {
                return ConnectionValidationResult.MISSING_CONNECTION;
            }
            if (destinationLevel == null) {
                return ConnectionValidationResult.MISSING_DESTINATION;
            }
            if (startLevel == null) {
                return ConnectionValidationResult.MISSING_START;
            }
            if (destinationLevel > startLevel) {
                return ConnectionValidationResult.CORRECT;
            }
            return ConnectionValidationResult.WRONG_ORDER;
        }

        public Integer getDepth(String stop) {
            int i = 0;
            for (Set<String> s : stopList) {
                if (s.contains(stop)) {
                    return i;
                }
                i++;
            }
            return null;
        }

        public void addStop(Integer level, String stop) {
            Set<String> stopLevel = null;
            try {
                stopLevel = stopList.get(level);
            } catch (IndexOutOfBoundsException iooe) {
                stopLevel = new HashSet<String>();
                stopList.add(level, stopLevel);
            }
            stopLevel.add(stop);
        }

        public void removeStop(Integer level, String stop) {
            Set<String> stopLevel = stopList.get(level);
            if (stopLevel != null) {
                stopLevel.remove(stop);
            }
        }

        public void addStopAndLevel(Integer level, String stop) {
            Set<String> stopLevel = new HashSet<String>();
            stopList.add(level, stopLevel);
            stopLevel.add(stop);
        }
    }

    /**
     * Sorts the stops in the connections
     * @return a sorted list of stop names
     */
    public List<String> sort() {
        StopSorterTable sst = new StopSorterTable();
        List<String> orderedStationList = new LinkedList<String>();

        for (Connection c : connections) {
            switch (sst.isValid(c)) {
                case CORRECT:
                    break;
                case MISSING_CONNECTION:
                    sst.addStop(0, c.start);
                    sst.addStop(1, c.destination);
                    break;
                case MISSING_START:
                    // Should not happen!
                    Logger.getLogger(StopSorter.class.getName()).log(Level.WARNING, "Reference to a start stop that was not found as a destination.");
                    // sst.addStop(sst.getDepth(c.destination) - 1, c.start);
                    break;
                case MISSING_DESTINATION:
                    sst.addStop(sst.getDepth(c.start) + 1, c.destination);
                    break;
                case WRONG_ORDER:
                    int startDepth = sst.getDepth(c.start);
                    int destinationDepth = sst.getDepth(c.destination);
                    if (startDepth == destinationDepth) {
                        sst.removeStop(startDepth, c.start);
                        sst.addStopAndLevel(startDepth, c.start);
                    } else {
                        // Should not happen!
                        Logger.getLogger(StopSorter.class.getName()).log(Level.WARNING, "Cannot resolve: destination preceeds start.");
                    }
                    break;
            }
        }

        for (Set<String> s : sst.stopList) {
            for (String stopName : s) {
                orderedStationList.add(stopName);
            }
        }
        return orderedStationList;
    }
}
